package cn.hallele.leinsteining.ms.infrastructure.util.bean;

import cn.hallele.leinsteining.mc.infrastructure.metadata.tree.annotation.Tree;
import cn.hallele.leinsteining.mc.infrastructure.metadata.tree.annotation.TreeId;
import cn.hallele.leinsteining.mc.infrastructure.metadata.tree.annotation.TreeParentId;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * TreeUtil
 *
 * @author anle5
 * @since 2024/5/31 21:33
 */
@Slf4j
public class TreeUtil {
    /**
     * 通过Map方式构建树
     *
     * @param list           列表
     * @param topParentIdVal 顶层节点父id的值
     * @return TreeList
     */
    public static <T> List<T> buildTreeByMap(List<T> list, Object topParentIdVal) {
        if (CollectionUtils.isEmpty(list)) {
            log.warn("List is empty");
            return new ArrayList<>();
        }

        Class<?> aClass = list.get(0).getClass();
        Field id = null;
        Field parentId = null;
        Field children = null;
        for (Field field : aClass.getDeclaredFields()) {
            if (id != null && parentId != null && children != null) {
                break;
            }

            if (field.getAnnotation(Tree.class) != null && children == null) {
                children = field;
            }

            if (field.getAnnotation(TreeParentId.class) != null && parentId == null) {
                parentId = field;
            }

            if (field.getAnnotation(TreeId.class) != null && id == null) {
                id = field;
            }
        }

        if (id == null || parentId == null || children == null) {
            log.error("List can not to Tree Map");
            throw new UnsupportedOperationException("List can not to Tree Map");
        }

        return getTreeList(list, topParentIdVal, parentId, id, children);
    }

    /**
     * @param list           list
     * @param topParentIdVal topParentIdVal
     * @param parentIdName   parentIdName
     * @param idName         idName
     * @param childrenName   childrenName
     * @param <T>            T
     * @return TreeList
     */
    private static <T> List<T> getTreeList(List<T> list, Object topParentIdVal, Field parentIdName, Field idName, Field childrenName) {
        Map<String, List<T>> mapList = list.stream()
                .collect(Collectors.groupingBy(o -> getFieldValue(o, parentIdName).toString()));
        list.forEach(node -> setFieldValue(node, mapList.get(getFieldValue(node, idName).toString()), childrenName.getName()));
        return list.stream()
                .filter(o -> Objects.equals(topParentIdVal, getFieldValue(o, parentIdName)))
                .collect(Collectors.toList());
    }

    /**
     * 获取属性值
     *
     * @param o     对象
     * @param field 属性
     * @return Object
     */
    private static Object getFieldValue(Object o, Field field) {
        try {
            field.setAccessible(true);
            return field.get(o);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置字段值
     *
     * @param o         对象
     * @param val       值
     * @param fieldName 属性名
     */
    private static void setFieldValue(Object o, Object val, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(o, val);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
